package lab.craft;

import java.io.CharArrayReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * 简单的手写词法分析器
 */
public class SimpleLexer {
    public static void main(String args[]) {
        SimpleLexer lexer = new SimpleLexer();
        String script = "int age = 45";
        System.out.println("parse: " + script);
       SimpleTokenReader tokenReader = lexer.tokenize(script);
       dump(tokenReader);

       // 测试 inta
        script = "inta age = 45;";
        System.out.println("\nparse :" + script);
        tokenReader = lexer.tokenize(script);
        dump(tokenReader);

       // 测试 in
        script = "in age = 45;";
        System.out.println("\nparse :" + script);
        tokenReader = lexer.tokenize(script);
        dump(tokenReader);

        // 测试 >=
        script = "age >= 45;";
        System.out.println("\nparse :" + script);
        tokenReader = lexer.tokenize(script);
        dump(tokenReader);

        // 测试 >
        script = "age > 45;";
        System.out.println("\nparse :" + script);
        tokenReader = lexer.tokenize(script);
        dump(tokenReader);
    }

    //临时保存token的文本
    private StringBuffer tokenText = null;
    //解析出来的 token
    private List<Token> tokens = null;
    //正在解析的token
    private SimpleToken token = null;

    //是否是字母
    private boolean isAlpha(int ch) {
        return ch >= 'a' && ch <= 'z' || ch >= 'A' && ch <= 'Z';
    }

    //是否是数字
    private boolean isDigit(int ch) {
        return ch >= '0' && ch <= '9';
    }

    //是否是空白字符
    private boolean isBlank(int ch) {
        return ch == ' ' || ch == '\t' || ch == '\n';
    }

    public static void dump(SimpleTokenReader tokenReader) {
        System.out.println("text\ttype");
        Token token = null;
        while ((token = tokenReader.read()) != null) {
            System.out.println(token.getText()+"\t\t"+token.getType());
        }
    }

    /**
     * 有限状态机进入初始状态
     * 某个 Token 解析完毕,也进入初始状态,将 Token 记下来,然后建立一个新的 Token
     * @param ch 下-字符
     * @return 状态机
     */
    private DFAState initToken(char ch) {
        if (tokenText.length() > 0) {
            token.setText(tokenText.toString());
            tokens.add(token);
            tokenText = new StringBuffer();
            token = new SimpleToken();
        }
       // 初始状态
        DFAState newState = DFAState.Initial;
        // 新的类型
        TokenType tokenType = null;
        if (isAlpha(ch)) {
            // 第一个字符是字母
            if (ch == 'i') {
                newState = DFAState.Id_int1;
            } else {
                newState = DFAState.Id;
            }
            tokenType = TokenType.Identifier;
        } else if (isDigit(ch)) {
            // 第一个字符是数字
            newState = DFAState.IntLiteral;
            tokenType = TokenType.IntLiteral;
        } else if (ch == '>') {
            // 第一个字符是>
            newState = DFAState.GT;
            tokenType = TokenType.GT;
        } else if (ch == '+') {
            newState = DFAState.Plus;
            tokenType = TokenType.Plus;
        } else if (ch == '-') {
            newState = DFAState.Minus;
            tokenType = TokenType.Minus;
        } else if (ch == '*') {
            newState = DFAState.Star;
            tokenType = TokenType.Star;
        } else if (ch == '/') {
            newState = DFAState.Slash;
            tokenType = TokenType.Slash;
        } else if (ch == ';') {
            newState = DFAState.SemiColon;
            tokenType = TokenType.SemiColon;
        } else if (ch == '(') {
            newState = DFAState.LeftParen;
            tokenType = TokenType.LeftParen;
        } else if (ch == ')') {
            newState = DFAState.RightParen;
            tokenType = TokenType.RightParen;
        } else if (ch == '=') {
            newState = DFAState.Assignment;
            tokenType = TokenType.Assignment;
        }
        if (tokenType != null) {
            // 合法字符
            token.setType(tokenType);
            tokenText.append(ch);
        }
        return newState;
    }


    /**
     * 解析字符串, 形成 Token.
     * 这是个有限状态自动机,在不同的状态中迁移
     * @param code
     * @return
     */
    public SimpleTokenReader tokenize(String code) {
        tokens = new ArrayList<>();
        CharArrayReader reader = new CharArrayReader(code.toCharArray());
        tokenText = new StringBuffer();
        token = new SimpleToken();
        int ich = 0;
        char ch = 0;
        DFAState state = DFAState.Initial;
        try {
            while ((ich = reader.read()) != -1) {
                ch = (char) ich;
                switch (state) {
                    case Id:
                        if (isAlpha(ch) || isDigit(ch)) {
                            // 保持标识符状态
                            tokenText.append(ch);
                        } else {
                            state = initToken(ch);
                        }
                        break;
                    case GT:
                        if (ch == '=') {
                            token.setType(TokenType.GE);
                            state = DFAState.GE;
                            tokenText.append(ch);
                        } else {
                            state = initToken(ch);
                        }
                        break;
                    case Initial:
                    case GE:
                    case Assignment:
                    case Plus:
                    case Minus:
                    case Star:
                    case Slash:
                    case SemiColon:
                    case LeftParen:
                    case RightParen:
                        // 退出当前状态,保存Token, 重新确定后续状态
                        state = initToken(ch);
                        break;
                    case IntLiteral:
                        if (isDigit(ch)) {
                            // 继续查找数字
                            tokenText.append(ch);
                        } else {
                            state = initToken(ch);
                        }
                        break;
                    case Id_int1:
                        if (ch == 'n') {
                            state = DFAState.Id_int2;
                            tokenText.append(ch);
                        } else if (isDigit(ch) || isAlpha(ch)) {
                            // 切回id,不是 int
                            state = DFAState.Id;
                            tokenText.append(ch);
                        } else {
                            state = initToken(ch);
                        }
                        break;
                    case Id_int2:
                        if (ch == 't') {
                            state = DFAState.Id_int3;
                            tokenText.append(ch);
                        } else if (isDigit(ch) || isAlpha(ch)) {
                            // 切回id,不是 int
                            state = DFAState.Id;
                            tokenText.append(ch);
                        } else {
                            state = initToken(ch);
                        }
                        break;
                    case Id_int3:
                        if (isBlank(ch)) {
                            token.setType(TokenType.Int);
                            state = initToken(ch);
                        } else {
                            state = DFAState.Id;
                            tokenText.append(ch);
                        }
                        break;
                    default:
                        // 忽略其它字符
                }
            }
            // 保存最后一个 token
            if (tokenText.length() > 0) {
                initToken(ch);
            }
        }catch (IOException e) {
            e.printStackTrace();
        }
        return new SimpleTokenReader(tokens);
    }
}
